package com.itheima.mp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.itheima.mp.domain.po.Address;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.query.UserQuery;
import com.itheima.mp.domain.vo.AddressVO;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.service.IUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements IUserService {
    @Override
    @Transactional
    public void deductBalance(Long id, Integer money) {
        User byId = getById(id);
        if(byId==null||byId.getStatus()==2){
            throw new RuntimeException("用户不存在或已被冻结");
        }
        if(byId.getBalance()<money){
            throw new RuntimeException("余额不足");
        }
//        baseMapper.deductBalance(id,money);
        lambdaUpdate()
                .set(User::getBalance,byId.getBalance()-money)
                .set(byId.getBalance()-money==0,User::getStatus,2)
                .eq(User::getId,id)
                .eq(User::getStatus,byId.getStatus())//乐观锁
                .update();
    }

    @Override
    public List<User> queryUsers(UserQuery userQuery) {
        return lambdaQuery()
                .like(userQuery.getName()!=null,User::getUsername,userQuery.getName())
                .eq(userQuery.getStatus()!=null,User::getStatus,userQuery.getStatus())
                .ge(userQuery.getMinBalance()!=null,User::getBalance,userQuery.getMinBalance())
                .le(userQuery.getMaxBalance()!=null,User::getBalance,userQuery.getMaxBalance()).list();
    }

    @Override
    public UserVO getUserVOById(Long id) {
        User byId = getById(id);
        if(byId==null||byId.getStatus()==2){
            throw new RuntimeException("用户不存在或已被冻结");
        }
        List<Address> list = Db.lambdaQuery(Address.class)
                .eq(Address::getUserId, id)
                .list();
        UserVO userVO = BeanUtil.copyProperties(byId, UserVO.class);
        if(CollUtil.isNotEmpty(list)){
            userVO.setAddresses(BeanUtil.copyToList(list, AddressVO.class));
        }
        return userVO;
    }

    @Override
    public List<UserVO> getUserVOByIds(List<Long> ids) {
        //根据ids查询用户
        List<User> users = listByIds(ids);
        if(CollUtil.isEmpty(users)){
            return Collections.emptyList();
        }
        //根据查询的用户获得准确的ids
        List<Long> collectid = users.stream().map(User::getId).collect(Collectors.toList());
        //使用Db根据ids查询地址
        List<Address> addresses = Db.lambdaQuery(Address.class)
                .in(Address::getUserId, collectid)
                .list();

        List<AddressVO> addressVOS = BeanUtil.copyToList(addresses, AddressVO.class);
        //根据地址的userId分组
        Map<Long, List<AddressVO>> collect = addressVOS.stream().collect(Collectors.groupingBy(AddressVO::getUserId));

        List<UserVO> userVOS = new ArrayList<>(users.size());
        /**
         * 先 add 再 set 可以避免这些风险，原因如下：
         *
         * 确保对象存在：首先将对象添加到集合中，确保对象已经存在于集合中。这样可以避免在后续操作中出现对象未添加到集合中的情况，从而减少潜在的错误。
         *
         * 简化异常处理：如果在 set 属性时发生异常，由于对象已经被添加到集合中，可以更容易地进行异常处理和调试，确保对象不会丢失。
         *
         * 逻辑清晰：先 add 再 set 的顺序使得代码的逻辑流程更加清晰，便于理解和维护。这种顺序符合一般的编程习惯，减少潜在的逻辑错误。
         *
         * 先 set 再 add 可能会导致在设置属性时出现异常或逻辑错误，从而导致对象没有被正确添加到集合中的原因如下：
         *
         * 异常处理：在设置属性时，可能会涉及到一些复杂的逻辑或外部资源的访问（例如数据库查询、网络请求等）。这些操作可能会抛出异常，如果在 set 属性时发生异常，对象可能还没有被添加到集合中，从而导致对象丢失。
         *
         * 逻辑错误：在设置属性时，可能会依赖于某些条件或状态。如果在这些条件或状态不满足的
         */
        for (User user : users) {
            UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
            userVOS.add(userVO);
            userVO.setAddresses(collect.get(user.getId()));
        }
        return userVOS;
    }

}